// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此Go实现部分源自参考
// ANSI C实现，该实现带有以下注意事项：
// 
// rijndael alg fst。c 
// 
// 3.0版（2000年12月）
// 
// Rijndael密码的优化ANSI c代码（现在是AES）
// 
// /作者文森特·里杰曼<文森特。rijmen@esat.kuleuven.ac.be>作者安托恩·博塞莱尔。bosselaers@esat.kuleuven.ac.be>作者保罗·巴雷托。barreto@terra.com.br>
// 
// 此代码在此公开。
// 
// 本软件由作者“按原样”提供，任何明示的
// 或默示保证，包括但不限于默示的
// 适销性和特定用途适用性的保证
// 均不承担任何责任。在任何情况下，作者或撰稿人均不对任何直接、间接、偶然、特殊、惩戒性或间接损害承担责任（包括但不限于采购替代商品或服务；使用、数据或利润损失；或业务中断），无论是何种原因和任何责任理论，
// 无论是合同、严格责任还是因使用本软件而产生的任何形式的侵权行为（包括疏忽
// 或其他），
// 即使被告知可能发生此类损害。
// 
// 规范见FIPS 197，实施细节见Daemen和Rijmen的Rijndael提交文件
// 。
// https:
// https:

package aes

import (
	"encoding/binary"
)

// 使用扩展密钥xk将src中的一个块加密到dst中。
func encryptBlockGo(xk []uint32, dst, src []byte) {
	_ = src[15] // 早期边界检查
	s0 := binary.BigEndian.Uint32(src[0:4])
	s1 := binary.BigEndian.Uint32(src[4:8])
	s2 := binary.BigEndian.Uint32(src[8:12])
	s3 := binary.BigEndian.Uint32(src[12:16])

	// 第一轮仅用键输入XOR。
	s0 ^= xk[0]
	s1 ^= xk[1]
	s2 ^= xk[2]
	s3 ^= xk[3]

	// 中间回合使用桌子洗牌。
	// 轮数由扩展键的长度设置。
	nr := len(xk)/4 - 2 // /-2：上一个，下一个
	k := 4
	var t0, t1, t2, t3 uint32
	for r := 0; r < nr; r++ {
		t0 = xk[k+0] ^ te0[uint8(s0>>24)] ^ te1[uint8(s1>>16)] ^ te2[uint8(s2>>8)] ^ te3[uint8(s3)]
		t1 = xk[k+1] ^ te0[uint8(s1>>24)] ^ te1[uint8(s2>>16)] ^ te2[uint8(s3>>8)] ^ te3[uint8(s0)]
		t2 = xk[k+2] ^ te0[uint8(s2>>24)] ^ te1[uint8(s3>>16)] ^ te2[uint8(s0>>8)] ^ te3[uint8(s1)]
		t3 = xk[k+3] ^ te0[uint8(s3>>24)] ^ te1[uint8(s0>>16)] ^ te2[uint8(s1>>8)] ^ te3[uint8(s2)]
		k += 4
		s0, s1, s2, s3 = t0, t1, t2, t3
	}

	// 上一轮直接使用s-box和XOR生成输出。
	s0 = uint32(sbox0[t0>>24])<<24 | uint32(sbox0[t1>>16&0xff])<<16 | uint32(sbox0[t2>>8&0xff])<<8 | uint32(sbox0[t3&0xff])
	s1 = uint32(sbox0[t1>>24])<<24 | uint32(sbox0[t2>>16&0xff])<<16 | uint32(sbox0[t3>>8&0xff])<<8 | uint32(sbox0[t0&0xff])
	s2 = uint32(sbox0[t2>>24])<<24 | uint32(sbox0[t3>>16&0xff])<<16 | uint32(sbox0[t0>>8&0xff])<<8 | uint32(sbox0[t1&0xff])
	s3 = uint32(sbox0[t3>>24])<<24 | uint32(sbox0[t0>>16&0xff])<<16 | uint32(sbox0[t1>>8&0xff])<<8 | uint32(sbox0[t2&0xff])

	s0 ^= xk[k+0]
	s1 ^= xk[k+1]
	s2 ^= xk[k+2]
	s3 ^= xk[k+3]

	_ = dst[15] // 早期边界检查
	binary.BigEndian.PutUint32(dst[0:4], s0)
	binary.BigEndian.PutUint32(dst[4:8], s1)
	binary.BigEndian.PutUint32(dst[8:12], s2)
	binary.BigEndian.PutUint32(dst[12:16], s3)
}

// 使用扩展密钥xk将src中的一个块解密为dst。
func decryptBlockGo(xk []uint32, dst, src []byte) {
	_ = src[15] // 早期边界检查
	s0 := binary.BigEndian.Uint32(src[0:4])
	s1 := binary.BigEndian.Uint32(src[4:8])
	s2 := binary.BigEndian.Uint32(src[8:12])
	s3 := binary.BigEndian.Uint32(src[12:16])

	// 第一轮仅用键输入XOR。
	s0 ^= xk[0]
	s1 ^= xk[1]
	s2 ^= xk[2]
	s3 ^= xk[3]

	// 中间回合使用桌子洗牌。
	// 轮数由扩展键的长度设置。
	nr := len(xk)/4 - 2 // /-2：上一个，下一个
	k := 4
	var t0, t1, t2, t3 uint32
	for r := 0; r < nr; r++ {
		t0 = xk[k+0] ^ td0[uint8(s0>>24)] ^ td1[uint8(s3>>16)] ^ td2[uint8(s2>>8)] ^ td3[uint8(s1)]
		t1 = xk[k+1] ^ td0[uint8(s1>>24)] ^ td1[uint8(s0>>16)] ^ td2[uint8(s3>>8)] ^ td3[uint8(s2)]
		t2 = xk[k+2] ^ td0[uint8(s2>>24)] ^ td1[uint8(s1>>16)] ^ td2[uint8(s0>>8)] ^ td3[uint8(s3)]
		t3 = xk[k+3] ^ td0[uint8(s3>>24)] ^ td1[uint8(s2>>16)] ^ td2[uint8(s1>>8)] ^ td3[uint8(s0)]
		k += 4
		s0, s1, s2, s3 = t0, t1, t2, t3
	}

	// 上一轮直接使用s-box和XOR生成输出。
	s0 = uint32(sbox1[t0>>24])<<24 | uint32(sbox1[t3>>16&0xff])<<16 | uint32(sbox1[t2>>8&0xff])<<8 | uint32(sbox1[t1&0xff])
	s1 = uint32(sbox1[t1>>24])<<24 | uint32(sbox1[t0>>16&0xff])<<16 | uint32(sbox1[t3>>8&0xff])<<8 | uint32(sbox1[t2&0xff])
	s2 = uint32(sbox1[t2>>24])<<24 | uint32(sbox1[t1>>16&0xff])<<16 | uint32(sbox1[t0>>8&0xff])<<8 | uint32(sbox1[t3&0xff])
	s3 = uint32(sbox1[t3>>24])<<24 | uint32(sbox1[t2>>16&0xff])<<16 | uint32(sbox1[t1>>8&0xff])<<8 | uint32(sbox1[t0&0xff])

	s0 ^= xk[k+0]
	s1 ^= xk[k+1]
	s2 ^= xk[k+2]
	s3 ^= xk[k+3]

	_ = dst[15] // 早期边界检查
	binary.BigEndian.PutUint32(dst[0:4], s0)
	binary.BigEndian.PutUint32(dst[4:8], s1)
	binary.BigEndian.PutUint32(dst[8:12], s2)
	binary.BigEndian.PutUint32(dst[12:16], s3)
}

// 对w中的每个字节应用sbox0。
func subw(w uint32) uint32 {
	return uint32(sbox0[w>>24])<<24 |
		uint32(sbox0[w>>16&0xff])<<16 |
		uint32(sbox0[w>>8&0xff])<<8 |
		uint32(sbox0[w&0xff])
}

// 旋转
func rotw(w uint32) uint32 { return w<<8 | w>>24 }

// 密钥扩展算法。参见FIPS-197，图11。
// 他们的rcon[i]是我们的powx[i-1]<<24。
func expandKeyGo(key []byte, enc, dec []uint32) {
	// 加密密钥设置。
	var i int
	nk := len(key) / 4
	for i = 0; i < nk; i++ {
		enc[i] = binary.BigEndian.Uint32(key[4*i:])
	}
	for ; i < len(enc); i++ {
		t := enc[i-1]
		if i%nk == 0 {
			t = subw(rotw(t)) ^ (uint32(powx[i/nk-1]) << 24)
		} else if nk > 6 && i%nk == 4 {
			t = subw(t)
		}
		enc[i] = enc[i-nk] ^ t
	}

	// 从加密密钥中导出解密密钥。
	// 将enc中的4个字的圆键集反转，生成dec。
	// 除第一个和最后一个外的所有集都应用了MixColumn变换。
	if dec == nil {
		return
	}
	n := len(enc)
	for i := 0; i < n; i += 4 {
		ei := n - i - 4
		for j := 0; j < 4; j++ {
			x := enc[ei+j]
			if i > 0 && i+4 < n {
				x = td0[sbox0[x>>24]] ^ td1[sbox0[x>>16&0xff]] ^ td2[sbox0[x>>8&0xff]] ^ td3[sbox0[x&0xff]]
			}
			dec[i+j] = x
		}
	}
}
